﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Diagnostics;

namespace Johnny
{
    /// <summary>
    /// An alternative ColorConverter
    /// </summary>
    public class AltColorConverter : ColorConverter
    {
        /// <summary>
        /// Convert various format of strings to color. Note: This method hides and replaces base.ConvertFromString().
        /// </summary>
        /// <param name="colorString">Accepts formats like "255, 11, 22, 33"(a,r,g,b), "11, 22, 33"(r,g,b), "#0056ef"(#rgb), "#05e"(#rgb) and named colors (ex. "Blue", "Aqua", etc.)</param>
        public static new Color ConvertFromString(string colorString)
        {
            string pattern1 = @"^(?<a>\d{1,3}%?),\s+(?<r>\d{1,3}%?),\s+(?<g>\d{1,3}%?),\s+(?<b>\d{1,3}%?)$";
            Regex regColor1 = new Regex(pattern1);
            string pattern2 = @"^(?<r>\d{1,3}%?),\s+(?<g>\d{1,3}%?),\s+(?<b>\d{1,3}%?)$";
            Regex regColor2 = new Regex(pattern2);
            string pattern3 = @"^#(?<r>[0-9a-fA-F]{2})(?<g>[0-9a-fA-F]{2})(?<b>[0-9a-fA-F]{2})$";
            Regex regColor3 = new Regex(pattern3);
            string pattern4 = @"^#(?<r>[0-9a-fA-F])(?<g>[0-9a-fA-F])(?<b>[0-9a-fA-F])$";
            Regex regColor4 = new Regex(pattern4);

            Color result = Color.Black;
            if (regColor1.IsMatch(colorString)) // Check pattern 1 like "255, 30, 30, 120" or "100%, 50%, 127, 127"
            {
                Match m = regColor1.Match(colorString);
                int a = frameDecValue(m.Groups["a"].Value);
                int r = frameDecValue(m.Groups["r"].Value);
                int g = frameDecValue(m.Groups["g"].Value);
                int b = frameDecValue(m.Groups["b"].Value);
                result = Color.FromArgb(a, r, g, b);
            }
            else if (regColor2.IsMatch(colorString)) // Check pattern 2 like "211, 98, 120" or "50%, 127, 127"
            {
                Match m = regColor2.Match(colorString);
                int r = frameDecValue(m.Groups["r"].Value);
                int g = frameDecValue(m.Groups["g"].Value);
                int b = frameDecValue(m.Groups["b"].Value);
                result = Color.FromArgb(255, r, g, b);
            }
            else if (regColor3.IsMatch(colorString)) // Check pattern 3 like "#ffee33"
            {
                Match m = regColor3.Match(colorString);
                int r = frameDecValue(convertHexValue(m.Groups["r"].Value));
                int g = frameDecValue(convertHexValue(m.Groups["g"].Value));
                int b = frameDecValue(convertHexValue(m.Groups["b"].Value));
                result = Color.FromArgb(255, r, g, b);
            }
            else if (regColor4.IsMatch(colorString)) // Check pattern 4 like "#fe3"
            {
                Match m = regColor4.Match(colorString);
                int r = frameDecValue(convertShortHexValue(m.Groups["r"].Value));
                int g = frameDecValue(convertShortHexValue(m.Groups["g"].Value));
                int b = frameDecValue(convertShortHexValue(m.Groups["b"].Value));
                result = Color.FromArgb(255, r, g, b);
            }
            else
                result = Color.FromName(colorString);
            return result;
        }

#region Private methods


        /// <summary>
        /// Check and frame the input value in a reasonable range
        /// </summary>
        private static int frameDecValue(string colorValue)
        {
            Regex regPercentage = new Regex(@"^\d{1,3}%$");
            int value = 0;
            colorValue = colorValue.Trim();
            if (regPercentage.IsMatch(colorValue))
            {   // ex. Convert 50% to 127
                value = int.Parse(colorValue.Replace("%", string.Empty));
                value = (int)((float)value / 100 * 256) - 1; 
                value = frameDecValue(value);
            }
            else
                if (int.TryParse(colorValue, out value))
                    value = frameDecValue(value);
            return value;
        }

        /// <summary>
        /// Check and frame the input value in a reasonable range
        /// </summary>
        private static int frameDecValue(int colorValue)
        {
            // Frame the value in a reasonable range
            colorValue = (colorValue > 255) ? 255 : colorValue;
            colorValue = (colorValue < 0) ? 0 : colorValue;
            return colorValue;
        }

        /// <summary>
        /// Convert hex value to dec
        /// </summary>
        private static int convertHexValue(string colorValue)
        {
            int value = 0;
            try
            {
                value = Convert.ToInt32(colorValue.Trim(), 16);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("{0} error: Input \"{1}\" may be invalid. Exception message: {2}", 
                    "Johnny.AltColorConverter.convertHexValue()", colorValue, ex.Message);
            }
            return value;
        }

        /// <summary>
        /// Convert a single letter hex value to double letter format and then convert it to dec. For example, convert "F" to "FF" and then 255.
        /// </summary>
        /// <param name="colorValue">A single letter hex number range from 0 to F</param>
        private static int convertShortHexValue(string colorValue)
        {
            string letter = colorValue.Trim().Substring(0, 1).ToUpper();
            return convertHexValue(letter + letter);
        }

#endregion
    }
}
